(* Creme Caramel Compiler (Triple C) - Michele Caceffo - 2008 *)
(** This module contains all the informations about types, structures and classes that are used almost anywhere along the code; other specific objects are declared only where needed *)
open Exceptions;;
open Syntaxtree;;

(** Labels are numeric to simplify things *)
type label      =   int
(** Register is an index for temporary variables *)
type register   =   int
(** Offsets is how far the variable is from the stack pointer *)
type offset     =   int

(** The return type of subroutines *)
type return = 
        SRet of bType   (** For non-void subroutines *)
      | Void            (** For void subroutines *)

(** The Semantic type, used for semantic analisys *)
type sType =
        DInt           
      | DFloat
      | DBool           

(** Instructions for intermediate code *)
type inst_type =
      (* Value Copy *)
         GAP     (** Get array pointer*)
      |  GAV     (** Get array value*)
      |  SAP     (** Set array pointer*)
      |  SAV     (** Set array value*)
      |  CPY     (** Assignment *)
      | AGET    (** Get from array *)
      | ASET    (** Set into array *)
      (* Arithmetical Operations *)
      | ADD     (** Sum *)
      | SUB     (** Subtraction *)
      | MUL     (** Multiplication *)
      | DIV     (** Quotient *)
      (* Logical Operations *)
      | AND     (** Logical AND *)
      | OR      (** Logical OR *)
      | NOT     (** Logical NOT *)
      (* Comparison *)
      | CEQU    (** Compare if Equals *)
      | CNE     (** Compare if Not Equals *)
      | CLE     (** Compare if Lower or  Equals *)
      | CG      (** Compare if Greater *)
      | CGE     (** Compare if Greater or Equals *)
      (* Jumps *)
      | JNE     (** Jump if Not Equals *)
      | JIE     (** Jump if Equals *)
      | GOTO    (** Goto *)
      (* Subroutines *)
      | PARAM   (** Parameter for subroutine *)
      | CALL    (** Subroutines Call *)
      | RET     (** Return for subroutines *)
      (* System functions *)
      | NOP     (** No Operation *)
      | HALT    (** End of program *)
      | OUT     (** Write on standard Output *)

(** Type of values *)
type value =
        I of int
      | F of float

(** Element of intermediate code instruction *)
type element =
        Reg of register * sType         (** Register (or temporary variable) with his index and his type *)
      | Off of offset * sType * bool    (** Offset with his gap value, his type and a bool which indicates 
                                        if is a variable defined on a subroutine or the main *)
      | Label of label                  (** Label for a code point *)
      | Val of value                    (** Numerical value *)
      | Subr of string                  (** Subroutine name *)
      | Null                            (** Null element *)

(** The instruction code, the first operand, the second operand, the destination element*)
type instruction = inst_type * element * element * element

(** The class contains all the method and objects which differ from one subroutine and the other, 
or the main program *)
class funUtils = object(self)
    (** The intermediate code instruction list *)
    val mutable inl         =   []
    (** The table which bind an instruction number with a Label *)
    val mutable label_table =   (Hashtbl.create 0)
    (** The label counter *)
    val mutable labelC      =   0
    (** The offset counter (which is the next offset for a new variable) *)
    val mutable offcount    =   -1

    (** Add an instruction to the list
        @param i The instruction to be added
    *)
    method addInstr (i: instruction)    =   inl <- inl @ [i]; ()
    (** Get the i-th instruction from the list
        @param i The position number
        @return The instruction at position i
    *)
    method getInstr (i: int)            =   List.nth inl i
    (** Get all the instruction list 
        @return The instruction list
    *)
    method getInl   = inl
    (** Get the instruction counter (how many instructions have been added)
        @return The number of added instructions
    *)
    method getIc    = List.length inl
    (** Return the next offset from the stack pointer 
        @return The next offset
    *)
    method stack : offset   =   offcount <- offcount+1; offcount
    
    (** Increments the label counter
        @return the next label index
    *)
    method incLabel =   labelC <- labelC + 1; labelC
    (** Add a new label to the table
        @param lab The label to add
        @param ip The instruction pointer to binf with the label  
    *)
    method addLabel (lab: element) (ip: int)    =   (Hashtbl.add label_table ip lab)
    (** Create a new label
        @return A new Label
    *)
    method getLabel                             =   (Label(self#incLabel))
    (** Check if an instruction is binded with a label
        @param i The index to check
        @return True if the instruction is binded with a label, false otherwise
    *)
    method hasLabel (i: int)                    =   (Hashtbl.mem label_table i)
    (** Retrive the label binded with an instruction
        @param i The instruction associated with a label
        @return The label binded with the instruction
    *)
    method getInstrLabel (i: int)               =   (Hashtbl.find label_table i)
end;;

(** This class is a over-structure for the funUtils class, and has methods for the manage of global variables, like registers, which are indipendent from the function (main or subroutines) that is processed*)
class intermediateCode = object(self)
    (** The instance of the funUtils class created by default *)
    val mutable il          = new funUtils
    (** The variable which count how many registers have been used *)
    val mutable reg         = 0

    (** A wrapper for the getIc method of the funUtils class 
        @return The instruction counter
    *)
    method getIc                =   il#getIc
    
    (** A wrapper for the getIc method of the funUtils class 
        @return The instruction list
    *)
    method getInstructionList   =   il#getInl
    
    (** Setter method which allows substitution of the funUtils class 
        @param newil The new funUtils instance
    *)
    method setInstructionClass (newil: funUtils)   =   il <- newil
    
    (** Getter method  of the funUtils class 
        @return the funUtils instance
    *)
    method getInstructionClass  = il

    (** Add a new instruction, calling the addInstr
        method of the funUtils class
        @param i The instruction to add
    *)
    method addInstr (i: instruction)    =   il#addInstr i
    
    (** Get the instruction at position i, 
        calling the getInstr method of the funUtils class
        @param i The instruction index to get
        @return The instruction at index i
    *)
    method getInstr (i: int)            =   il#getInstr i

    (** Get the next offset for a variable
        @return The next available offset
    *)
    method stack            =   il#stack
    
    (** Get the next available register index
        @return The next register index
    *)
    method getReg :register =   reg <- reg+1; reg

    (** Wrapper for the addLabel method
        @param lab The element (a label) to bind with the instruction
        @param ip The number of the instruction where the label is binded
    *)
    method addLabel (lab: element) (ip: int)    =   il#addLabel lab ip

    (** Retrive the label binded with an instruction
        @param i The instruction associated with a label
        @return The label binded with the instruction
    *)
    method getLabel                             =   il#getLabel
    
    (** Check if an instruction is binded with a label
        @param i The index to check
        @return True if the instruction is binded with a label, false otherwise
    *)
    method hasLabel (i: int)                    =   il#hasLabel i

    (** Retrive the label binded with an instruction
        @param i The instruction associated with a label
        @return The label binded with the instruction
    *)
    method getInstrLabel (i: int)               =   il#getInstrLabel i

end;;

(** A dec_table binds an ide (variable name) with 3 elements: 2 integers
    (x and y coordinate) and the element associated with the variable
*)
type dec_table = ((ide,(int * int * element)) Hashtbl.t)

(** A proc_entry can be in Building phase (which has the parameter 
    types of the call and the return type) or a Subroutines (which 
    contains all the informations for that function/procedure, such 
    as the local variables declaration and the funUtils class for that
    subroutine)
*)
type proc_entry = 
        Building of return * dec list
      | Subroutine of return * dec list * dec_table * funUtils

(** The proc_table binds the name of a subroutine with its proc_entry *)
type proc_table = ((ide, proc_entry) Hashtbl.t)
